• Jump To … +
    main.js separate.js single.js web-apg-api.js main.js web-conv-api.js ast.js csv.js dangling-else.js display.js flags.js float.js limits.js main.js multiline-mode.js recursive.js replace.js rules.js split.js testonly.js trace.js udt.js unicode.js web-email.js word-boundaries.js main.js phone-number.js web-main.js web-phone-number.js main.js phone-number.js setup.js translate.js xml.js branch-fail-grammar.js main.js parent-mode-grammar.js setup.js universal-mode-grammar.js colors-app.js colors-callbacks.js colors.js main.js more-app.js more-setup.js more.js ast-callbacks.js bad-input.js basic.js ini-file.js main.js parser-callbacks.js setup.js trace.js anbncn.js and.js c-comment.js compound.js main.js nested.js not.js setup.js boundaries-grammar.js boundaries.js comment-grammar.js comment.js main.js negative-grammar.js negative.js positive-grammar.js positive.js setup.js main.js odata-grammar.js run.js setup.js area-code.js lookaround.js main.js phone-number.js setup.js simple.js all-operators.js default.js fancy-number.js limited-lines.js main.js select-operators.js select-rules.js setup.js main.js minimal.js parent-u.js parent.js phone-number.js setup.js stats.js trace.js universal-u.js universal.js callbacks.js grammar.js main.js parser.js writeHtml.js LICENSE.md README.md index.md
  • callbacks.js

  • §
    /*  *************************************************************************************
     *   copyright: Copyright (c) 2025 Lowell D. Thomas, all rights reserved
     *     license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)
     *   ********************************************************************************* */
  • §

    These are the callback functions used by the URI parser. There is no reporting of specific errors. Any error will simply force the parser to fail. The user will need to trace(debug) the parse tree to find parsing errors

    const { apgLib } = require('apg-js');
    const ids = apgLib.ids;
    const utils = apgLib.utils;
    
    function setUriElement(data, key, value) {
      if (data && data.uriElements) {
        data.uriElements[key] = value;
      }
    }
    
    const cb = {
  • §

    The start rule for the URI parser. It simply collects the URI string on success and fails if the input string is empty.

      URI(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH) {
          data.uri = utils.charsToString(chars, phraseIndex, sys.phraseLength);
        } else if (sys.state === ids.EMPTY) {
          sys.state = ids.NOMATCH;
          sys.phraseLength = 0;
        }
      },
  • §

    Collect the URI scheme.

      scheme(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH) {
          setUriElement(data, 'scheme', utils.charsToString(chars, phraseIndex, sys.phraseLength));
        }
      },
  • §

    Match userinfo@ but reduce the phrase length to exclude the @.

      userinfoAt(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH) {
          setUriElement(data, 'userinfo', utils.charsToString(chars, phraseIndex, sys.phraseLength - 1));
        }
      },
  • §

    Collect the host name. If it is an IP-literal, remove the leading [ and trailing ].

      host(sys, chars, phraseIndex, data) {
        if (sys.state === ids.ACTIVE) {
          data.iplit = false;
        } else if (sys.state === ids.MATCH) {
          if (data.iplit) {
            setUriElement(data, 'host', utils.charsToString(chars, phraseIndex + 1, sys.phraseLength - 2));
          } else {
            setUriElement(data, 'host', utils.charsToString(chars, phraseIndex, sys.phraseLength));
          }
        } else if (sys.state === ids.EMPTY) {
          setUriElement(data, 'host', '');
        }
      },
  • §

    If this is an IP-literal, set the flag.

      ipLiteral(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH) {
          data.iplit = true;
        }
      },
  • §

    Collect the port number. If it is a number, convert the string to a number. If it is empty, set the port to the empty string ''.

      port(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH) {
          const portStr = utils.charsToString(chars, phraseIndex, sys.phraseLength);
          const parsed = parseInt(portStr, 10);
          if (Number.isNaN(parsed)) {
            sys.state = ids.NOMATCH;
            sys.phraseLength = 0;
          } else {
            setUriElement(data, 'port', parsed);
          }
        } else if (sys.state === ids.EMPTY) {
          setUriElement(data, 'port', '');
        }
      },
  • §

    The followng four rules are used to collect the path.

      pathAbempty(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH) {
          setUriElement(data, 'path', utils.charsToString(chars, phraseIndex, sys.phraseLength));
        } else if (sys.state === ids.EMPTY) {
          setUriElement(data, 'path', '');
        }
      },
      pathAbsolute(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH) {
          setUriElement(data, 'path', utils.charsToString(chars, phraseIndex, sys.phraseLength));
        }
      },
    
      pathRootless(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH) {
          setUriElement(data, 'path', utils.charsToString(chars, phraseIndex, sys.phraseLength));
        }
      },
    
      pathEmpty(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH || sys.state === ids.NOMATCH) {
          sys.state = ids.NOMATCH;
          sys.phraseLength = 0;
        }
        if (sys.state === ids.EMPTY) {
          setUriElement(data, 'path', '');
        }
      },
  • §

    Get the query string.

      query(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH) {
          setUriElement(data, 'query', utils.charsToString(chars, phraseIndex, sys.phraseLength));
        } else if (sys.state === ids.EMPTY) {
          setUriElement(data, 'query', '');
        }
      },
  • §

    Get the fragment string.

      fragment(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH) {
          setUriElement(data, 'fragment', utils.charsToString(chars, phraseIndex, sys.phraseLength));
        } else if (sys.state === ids.EMPTY) {
          setUriElement(data, 'fragment', '');
        }
      },
  • §

    If this is an IPv4 address, set the flag.

      ipv4(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH) {
          data.ipv4 = true;
        }
      },
  • §

    Count the number of 16-bit hex digits.

      h16(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH) {
          data.h16count += 1;
        }
      },
  • §

    Collect the IPv6 address if in does not contain a double colon. Semantically validate the number of 16-bit digits.

      nodcolon(sys, chars, phraseIndex, data) {
        if (sys.state === ids.ACTIVE) {
          data.h16count = 0;
          data.ipv4 = false;
        } else if (sys.state === ids.MATCH) {
          if (data.ipv4 ? data.h16count === 6 : data.h16count === 8) {
            sys.state = ids.MATCH;
          } else {
            sys.state = ids.NOMATCH;
            sys.phraseLength = 0;
          }
        }
      },
  • §

    Collect the IPv6 address if in does contains a double colon. Semantically validate the number of 16-bit digits.

      dcolon(sys, chars, phraseIndex, data) {
        if (sys.state === ids.ACTIVE) {
          data.h16count = 0;
          data.ipv4 = false;
        } else if (sys.state === ids.MATCH) {
          if (data.ipv4 ? data.h16count < 6 : data.h16count < 8) {
            sys.state = ids.MATCH;
          } else {
            sys.state = ids.NOMATCH;
            sys.phraseLength = 0;
          }
        }
      },
  • §

    Collect the decimal octet and validate it semantically. The octet is a number between 0 and 255.

      decOctet(sys, chars, phraseIndex, data) {
        if (sys.state === ids.ACTIVE) {
          data.octet = 0;
        } else if (sys.state === ids.MATCH) {
          if (data.octet > 255) {
            sys.state = ids.NOMATCH;
            sys.phraseLength = 0;
          }
        }
      },
  • §

    Get the numeric value of the decimal octet.

      decDigit(sys, chars, phraseIndex, data) {
        if (sys.state === ids.MATCH) {
          data.octet = 10 * data.octet + chars[phraseIndex] - 48;
        }
      },
    };
    
    module.exports = cb;